package cn.skyquant.quant4j.jforex.strategy.titans;

import cn.skyquant.quant4j.jforex.sdk.enums.LabelType;
import cn.skyquant.quant4j.jforex.sdk.enums.OpenType;
import cn.skyquant.quant4j.jforex.sdk.strategy.*;
import cn.skyquant.quant4j.jforex.sdk.strategy.BaseConfig;
import cn.skyquant.quant4j.sdk.enums.TradeDirection;
import cn.skyquant.quant4j.sdk.util.time.CalendarUtils;
import com.dukascopy.api.*;
import com.dukascopy.api.feed.IBarFeedListener;
import com.dukascopy.api.feed.IFeedDescriptor;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Library("quant4j-api.jar;quant4j-sdk.jar;quant4j-jforex-sdk.jar;freemarker-2.3.28.jar;httpclient-4.5.6.jar;httpcore-4.4.10.jar;fastjson-1.2.49.jar;commons-codec-1.11.jar")
public class Titans002 extends BaseStrategy implements IBarFeedListener {

    @Configurable("货币")
    public Instrument i1 = Instrument.EURUSD;

    @Configurable("★入场条件选择1.顺大趋势,逆小趋势买入★")
    public boolean _in_1 = true;
    @Configurable("大趋势判断:上根K柱收盘高于n根K柱移动均价")
    public int _n = 30;
    @Configurable("大趋势判断:上根K柱收盘高于n根K柱移动均价x倍大ATR")
    public double _x = 1.2;
    @Configurable("小趋势判断:上根K柱收盘低于m根K柱最高价")
    public int _m = 7;
    @Configurable("小趋势判断:上根K柱收盘低于m根K柱最高价y倍大ATR")
    public double _y = 1.5;
    @Configurable("重回大趋势:上根K柱收盘+(m根K柱最高价-昨日收盘)*z")
    public double _z = 0.5;

    @Configurable("★入场条件选择2.横盘突然拉升时买入★")
    public boolean _in_2 = true;
    @Configurable("横盘判断,大ATR计算K柱个数")
    public int _b_atr_days = 50;
    @Configurable("横盘判断,小ATR计算K柱个数")
    public int _s_atr_days = 10;
    @Configurable("横盘判断,大ATR是小ATR的s倍")
    public double _b_s_atr = 1.2;
    @Configurable("拉升判断,当前价格高于上根K柱收盘价w倍小ATR")
    public double _w = 1.5;

    @Configurable("★止盈条件:固定f倍大ATR★")
    public String _f = "1:1,2:2,3:3,4:4,5:1,6:1,7:1";

    @Configurable("★出场条件1.吊灯法移动止损★")
    public boolean _out_1 = true;
    @Configurable("吊灯法入市后最高价-a倍大ATR")
    public double _a = 3;
    @Configurable("★出场条件2.YOYO法移动止损★")
    public boolean _out_2 = true;
    @Configurable("YOYO:上根K柱收盘价-b倍大ATR")
    public double _b = 1.5;
    @Configurable("★出场条件3.棘轮法移动止损★")
    public boolean _out_3 = true;
    @Configurable("棘轮法:m根K柱低点+持仓时间*d*大ATR")
    public double _d = 0.05;

    @Configurable("订单成交日志")
    public boolean log_fill = true;
    @Configurable("订单提交日志")
    public boolean log_submit = true;
    @Configurable("K柱开盘计算日")
    public boolean log_k = true;
    @Configurable("修改止损价日志")
    public boolean log_sl = true;


    final Map<Instrument, InstrumentEntity> insMap = new HashMap<>();//交易品种实体

    final Map<Instrument, Double> atr_big_map = new HashMap<>();
    final Map<Instrument, Double> atr_small_map = new HashMap<>();
    final Map<Instrument, Double> min_low_map = new HashMap<>();
    final Map<Instrument, Double> max_high_map = new HashMap<>();
    final Map<Instrument, Double> ma_map = new HashMap<>();
    double profit1 = 0;
    double profit2 = 0;


    @Override
    protected String getVersion() {
        return "002";
    }

    @Override
    protected String getName() {
        return "Titans" + eaId;
    }

    @Override
    protected String getEaName() {
        return "Titans";
    }

    @Override
    protected void onTickEx(Instrument instrument, ITick tick) {
    }

    @Override
    protected void onBarEx(Instrument instrument, Period period, IBar askBar, IBar bidBar) {
    }

    @Override
    protected void onAccountEx(IAccount account) {
    }

    int[] ps;
    double[] tps;

    @Override
    public void onStopEx() {
        super.onStopEx();
        out("1.profit=%.2f", profit1);
        out("2.profit=%.2f", profit2);
        atr_big_map.clear();
        atr_small_map.clear();
        insMap.clear();
        max_high_map.clear();
        ma_map.clear();
    }

    @Override
    protected void init(InitEntity initEntity) {
        TitansConfig titansConfig = new TitansConfig(getName(), 0, 1, 0.2, i1);
        initEntity.a.add(titansConfig);
        context.subscribeToBarsFeed(i1, period, OfferSide.BID, this);
        initEntity.r = true;
        insMap.put(i1, new InstrumentEntity(i1));
        IChart chart = context.getChart(i1);
        if (chart != null) {
            IFeedDescriptor feedDescriptor = chart.getFeedDescriptor();
            if (feedDescriptor != null) {
                feedDescriptor.setPeriod(period);
                feedDescriptor.setOfferSide(OfferSide.ASK);
                chart.setFeedDescriptor(feedDescriptor);
                if(_in_2){
                    chart.add(indicators.getIndicator("ATR"), new Object[]{_b_atr_days});
                    chart.add(indicators.getIndicator("ATR"), new Object[]{_s_atr_days});
                }
                chart.repaint();
            }
        }

        String[] fs = _f.split(",");
        ps = new int[fs.length];
        tps = new double[fs.length];
        for (int i = 0; i < fs.length; i++) {
            String[] fas = fs[i].split(":");
            tps[i] = Double.parseDouble(fas[0]);
            ps[i] = Integer.parseInt(fas[1]);
        }
    }

    @Override
    public void onBar(Instrument instrument, Period period, OfferSide offerSide, IBar bar) {
        try {
            process(instrument, bar);
        } catch (JFException e) {
            err("process error:%s", e.getMessage());
        }
    }

    private void process(Instrument instrument, IBar bar) throws JFException {
        ITimeDomain timeDomain0;
        ITimeDomain timeDomain1;
        try {
            timeDomain0 = dataService.getOfflineTimeDomain(0);
            timeDomain1 = dataService.getOfflineTimeDomain(-1);
        } catch (JFException e) {
            if (log_error) warn("onBar.getOfflineTimeDomain error:%s", e);
            return;
        }
        if (timeDomain0 == null || timeDomain1 == null) {
            if (log_error) warn("onBar.getOffline,TimeDomain is null");
            return;
        }
        //停盘时间不干活
        if (timeDomain1.getEnd() > context.getTime() || context.getTime() > timeDomain0.getStart()) {
            return;
        }

        long barstart = history.getBarStart(period, context.getTime());
        long barnextstart = history.getNextBarStart(period, context.getTime());
        if (!calc(instrument, barstart, barnextstart)) {
            return;
        }

        List<IOrder> orderList = engine.getOrders(instrument);
        for (BaseConfig config : configList) {
            if (!(config instanceof TitansConfig)) {
                continue;
            }
            TitansConfig titansConfig = (TitansConfig) config;
            if (titansConfig.instrument != instrument) {
                continue;
            }
            processByConfig(instrument, titansConfig, orderList, bar, barstart, barnextstart);
        }
    }

    //先计算关键指标
    private boolean calc(Instrument instrument, long barstart, long barnextstartt) {
        double[] atrs_b = null;
        double[] atrs_s = null;
        double min_low = -1;
        double max_high = 100000;
        double ma = 0;
        try {
            atrs_b = indicators.atr(instrument, period, OfferSide.ASK, _b_atr_days, Filter.WEEKENDS, 1, barstart, 0);
            atrs_s = indicators.atr(instrument, period, OfferSide.ASK, _s_atr_days, Filter.WEEKENDS, 1, barstart, 0);
            min_low = indicators.min(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.LOW, _m, 0);
            max_high = indicators.max(instrument, period, OfferSide.ASK, IIndicators.AppliedPrice.HIGH, _m, 0);
            ma = indicators.ma(instrument, period, OfferSide.ASK, IIndicators.AppliedPrice.WEIGHTED_CLOSE, _n, IIndicators.MaType.SMA, 0);
        } catch (JFException e) {
            if (log_error) warn("%s get atr error:%s", instrument.name(), e);
            return false;
        }
        if (atrs_b == null || atrs_b.length == 0 || atrs_b[0] == 0 || atrs_s == null || atrs_s.length == 0 || atrs_s[0] == 0
                || min_low <= 0 || max_high >= 99999
                || ma == 0) {
            return false;
        }
        double atr_big = atrs_b[0];
        double atr_small = atrs_s[0];
        if (log_k) {
            InstrumentEntity ie = insMap.get(instrument);
            out("%s,atr[%d]=%s,atr[%d]=%s,min[%d]=%s,max[%d]=%s,ma[%d]=%s",
                    period == Period.DAILY ? CalendarUtils.formatGMTSimple(barstart) : CalendarUtils.formatGMTStandard(barstart),
                    _b_atr_days, ie.formatPrice(atr_big),
                    _s_atr_days, ie.formatPrice(atr_small),
                    _m, min_low,
                    _m, max_high,
                    _n, ie.formatPrice(ma));
        }
        atr_big_map.put(instrument, atr_big);
        atr_small_map.put(instrument, atr_small);
        min_low_map.put(instrument, min_low);
        max_high_map.put(instrument, max_high);
        ma_map.put(instrument, ma);
        return true;
    }

    private void processByConfig(Instrument instrument, TitansConfig config, List<IOrder> orderList, IBar bar, long barstart, long barnextstart) throws JFException {
        OrderExManager orderExManager = getOrderExManager(config, orderList);
        //对于既有的单子做止损修改
        sl_change(instrument, orderExManager, barstart, bar);
        //入场判断1
        if (_in_1) {
            in1(instrument, orderExManager, bar, barstart, barnextstart);
        }
        //入场判断2
        if (_in_2) {
            in2(instrument, orderExManager, bar, barstart, barnextstart);
        }
    }

    private void sl_change(Instrument instrument, OrderExManager orderExManager, long barstart, IBar bar) {
        double atr_big = atr_big_map.get(instrument);
        InstrumentEntity ie = insMap.get(instrument);
        double last_close = bar.getClose();
        for (OrderEx orderEx : orderExManager.buyList) {
            if (orderEx == null || orderEx.order == null) {
                continue;
            }
            try {
                IOrder order = orderEx.order;
                double old_sl = orderEx.order.getStopLossPrice();
                double new_sl = old_sl;
                int sl_type = 0;
                long fill_time = order.getFillTime();
                long from = history.getBarStart(period, fill_time);
                if (from >= barstart) {
                    continue;
                }

                if (_out_1) {
                    //入市后的最高价-a倍atr
                    //先计算入市后到现在的最高价
                    List<IBar> barList = history.getBars(instrument, period, OfferSide.ASK, from, barstart);
                    double max_high = barList.stream().mapToDouble(IBar::getHigh).max().getAsDouble();
                    double buy_sl_1 = max_high - _a * atr_big;
                    if (buy_sl_1 > new_sl) {
                        new_sl = buy_sl_1;
                        sl_type = 1;
                    }
                }
                if (_out_2) {
                    double buy_sl_2 = last_close - _b * atr_big;
                    if (buy_sl_2 > new_sl) {
                        new_sl = buy_sl_2;
                        sl_type = 2;
                    }
                }
                if (_out_3) {
                    //计算C根K柱低点
                    double min_low = min_low_map.get(instrument);
                    long time_diff = (context.getTime() - fill_time) / period.getInterval();
                    double buy_sl_3 = min_low + time_diff * _d * atr_big;
                    if (buy_sl_3 > new_sl) {
                        new_sl = buy_sl_3;
                        sl_type = 3;
                    }
                }
                if (new_sl > old_sl) {
                    order.setStopLossPrice(new_sl);
                    if (log_sl)
                        out("[%s],change sl use[%d], from[%s] to[%s]:", order.getLabel(), sl_type, old_sl, ie.formatPrice(new_sl));
                }
            } catch (JFException e) {
                err("sl_change buy error:%s", e.getMessage());
                continue;
            }
        }
        for (OrderEx orderEx : orderExManager.sellList) {
            if (orderEx == null || orderEx.order == null) {
                continue;
            }
            try {
                IOrder order = orderEx.order;
                double old_sl = orderEx.order.getStopLossPrice();
                double new_sl = old_sl;
                int sl_type = 0;
                long fill_time = order.getFillTime();
                long from = history.getBarStart(period, fill_time);
                if (from >= barstart) {
                    continue;
                }

                if (_out_1) {
                    //入市后的最低价+a倍atr
                    //先计算入市后到现在的最低价
                    List<IBar> barList = history.getBars(instrument, period, OfferSide.BID, from, barstart);
                    double min_low = barList.stream().mapToDouble(IBar::getLow).min().getAsDouble();
                    double sell_sl_1 = min_low + _a * atr_big;
                    if (sell_sl_1 < new_sl) {
                        new_sl = sell_sl_1;
                        sl_type = 1;
                    }
                }
                if (_out_2) {
                    double sell_sl_2 = last_close + _b * atr_big;
                    if (sell_sl_2 < new_sl) {
                        new_sl = sell_sl_2;
                        sl_type = 2;
                    }
                }
                if (_out_3) {
                    //计算C根K柱高
                    double max_high = max_high_map.get(instrument);
                    long time_diff = (context.getTime() - fill_time) / period.getInterval();
                    double sell_sl_3 = max_high - time_diff * _d * atr_big;
                    if (sell_sl_3 < new_sl) {
                        new_sl = sell_sl_3;
                        sl_type = 3;
                    }
                }
                if (new_sl < old_sl) {
                    order.setStopLossPrice(new_sl);
                    if (log_sl)
                        out("[%s],change sl use[%d], from[%s] to[%s]:", order.getLabel(), sl_type, old_sl, ie.formatPrice(new_sl));
                }
            } catch (JFException e) {
                err("sl_change sell error:%s", e.getMessage());
                continue;
            }
        }
    }

    private void in1(Instrument instrument, OrderExManager orderExManager, IBar bar, long barstart, long barnextstart) {
        double ma = ma_map.get(instrument);
        double min_low = min_low_map.get(instrument);
        double max_high = max_high_map.get(instrument);
        double atr_big = atr_big_map.get(instrument);
        double last_close = bar.getClose();
        InstrumentEntity ie = insMap.get(instrument);
        if (last_close > ma + _x * atr_big //收盘价高于移动均价x倍波动率
                && last_close < max_high - _y * atr_big //收盘价低于最高价y倍波动率
        ) {
            double buy_price = last_close + _z * atr_big;//昨日收盘再向上突破z倍开多单
            openBuyOrder(1, orderExManager, instrument, buy_price, atr_big, last_close, barnextstart);
            if (log_submit) {
                out("in:1 last_close[%s] > ma[%s] + x[%s]*atr[%s] = %s,last_close[%s] < max_high[%s] - y[%s]*atr[%s]=%s",
                        last_close, ie.formatPrice(ma), _x, ie.formatPrice(atr_big), ie.formatPrice(ma + _x * atr_big), last_close, ie.formatPrice(max_high), _y, ie.formatPrice(atr_big), ie.formatPrice(max_high - _y * atr_big));
            }
        }

        if (last_close < ma - _x * atr_big //收盘价低于移动均价x倍波动率
                && last_close > min_low + _y * atr_big //收盘价高于最低价y倍波动率
        ) {
            double sell_price = last_close - _z * atr_big;//昨日收盘再向下突破z倍开空单
            openSellOrder(1, orderExManager, instrument, sell_price, atr_big, last_close, barnextstart);

            if (log_submit) {
                out("in:1 last_close[%s] < ma[%s] - x[%s]*atr[%s] = %s,last_close[%s] > min_low[%s] + y[%s]*atr[%s]=%s",
                        last_close, ie.formatPrice(ma), _x, ie.formatPrice(atr_big), ie.formatPrice(ma + _x * atr_big), last_close, ie.formatPrice(min_low), _y, ie.formatPrice(atr_big), ie.formatPrice(min_low + _y * atr_big));
            }
        }
    }

    private void in2(Instrument instrument, OrderExManager orderExManager, IBar bar, long barstart, long barnextstart) {
        InstrumentEntity ie = insMap.get(instrument);
        double atr_big = atr_big_map.get(instrument);
        double atr_small = atr_small_map.get(instrument);
        if (atr_big < atr_small * _b_s_atr) {
            return;
        }
        double buy_price = new BigDecimal(bar.getClose() + _w * atr_small).setScale(ie.scale, RoundingMode.UP).doubleValue();
        openBuyOrder(2, orderExManager, instrument, buy_price, atr_big, bar.getClose(), barnextstart);
        double sell_price = new BigDecimal(bar.getClose() - _w * atr_small).setScale(ie.scale, RoundingMode.DOWN).doubleValue();
        openSellOrder(2, orderExManager, instrument, sell_price, atr_big, bar.getClose(), barnextstart);
    }

    private void openBuyOrder(int in_type, OrderExManager orderExManager, Instrument instrument, double buy_price, double atr_big, double last_close, long barnextstart) {
        InstrumentEntity ie = insMap.get(instrument);
        int buy_max_idx = -1;
        for (OrderEx b : orderExManager.buyList) {
            if (b.seriesIndex > buy_max_idx) {
                buy_max_idx = b.seriesIndex;
            }
        }
        for (OrderEx b : orderExManager.buyStopList) {
            if (b.seriesIndex > buy_max_idx) {
                buy_max_idx = b.seriesIndex;
            }
        }
        for (OrderEx b : orderExManager.buyLimitList) {
            if (b.seriesIndex > buy_max_idx) {
                buy_max_idx = b.seriesIndex;
            }
        }
        //止损的计算
        double buy_sl = 0;
        if (_out_1) {
            double buy_sl_1 = buy_price - _a * atr_big;
            buy_sl = buy_sl_1;
        }
        if (_out_2) {
            double buy_sl_2 = last_close - _b * atr_big;
            if (buy_sl_2 > buy_sl) {
                buy_sl = buy_sl_2;
            }
        }
        /*
        if (_out_3) {
            //计算C根K柱低点
            double min_low = min_low_map.get(instrument);
            if (min_low > buy_sl) {
                buy_sl = min_low;
            }
        }*/
        buy_price = new BigDecimal(buy_price).setScale(ie.scale, RoundingMode.DOWN).doubleValue();
        buy_sl = new BigDecimal(buy_sl).setScale(ie.scale, RoundingMode.UP).doubleValue();
        //止盈计算
        for (int i = 0; i < ps.length; i++) {
            double buy_tp = new BigDecimal(buy_price + atr_big * tps[i]).setScale(ie.scale, RoundingMode.UP).doubleValue();
            int seriesId = buy_max_idx + 1 + i;
            double amount = ie.minAmount * ps[i];
            OrderEx orderEx_buy = new OrderEx(getName(), LabelType.N, 0, seriesId, instrument, TradeDirection.LONG);
            TitansOrderComment orderComment_buy = new TitansOrderComment(in_type, seriesId, OpenType.P, (int) account.getEquity(), context.getTime(), 0);

            try {
                IOrder order_buy = engine.submitOrder(orderEx_buy.toString(), instrument, IEngine.OrderCommand.BUYSTOP, amount, buy_price, 0, buy_sl, buy_tp, barnextstart - 60000, orderComment_buy.toString());
                order_buy.waitForUpdate(2000, IOrder.State.OPENED);
            } catch (JFException e) {
                err("submit order. error:%s", e.getMessage());
            }
        }

    }

    private void openSellOrder(int in_type, OrderExManager orderExManager, Instrument instrument, double sell_price, double atr_big, double last_close, long barnextstart) {
        InstrumentEntity ie = insMap.get(instrument);
        int sell_max_idx = -1;
        for (OrderEx b : orderExManager.sellList) {
            if (b.seriesIndex > sell_max_idx) {
                sell_max_idx = b.seriesIndex;
            }
        }
        for (OrderEx b : orderExManager.sellStopList) {
            if (b.seriesIndex > sell_max_idx) {
                sell_max_idx = b.seriesIndex;
            }
        }
        for (OrderEx b : orderExManager.sellLimitList) {
            if (b.seriesIndex > sell_max_idx) {
                sell_max_idx = b.seriesIndex;
            }
        }
        //止损的计算
        double sell_sl = 20000;
        if (_out_1) {
            double sell_sl_1 = sell_price + _a * atr_big;
            sell_sl = sell_sl_1;
        }
        if (_out_2) {
            double sell_sl_2 = last_close + _b * atr_big;
            if (sell_sl_2 < sell_sl) {
                sell_sl = sell_sl_2;
            }
        }
        /*
        if (_out_3) {
            //计算C根K柱高点
            double max_high = max_high_map.get(instrument);
            if (max_high < sell_sl) {
                sell_sl = max_high;
            }
        }*/
        //止盈计算
        sell_price = new BigDecimal(sell_price).setScale(ie.scale, RoundingMode.UP).doubleValue();
        sell_sl = new BigDecimal(sell_sl).setScale(ie.scale, RoundingMode.DOWN).doubleValue();

        for (int i = 0; i < ps.length; i++) {
            int seriesId = sell_max_idx + 1 + i;
            double amount = ie.minAmount * ps[i];
            double sell_tp = new BigDecimal(sell_price - atr_big * tps[i]).setScale(ie.scale, RoundingMode.DOWN).doubleValue();
            OrderEx orderEx_sell = new OrderEx(getName(), LabelType.N, 0, seriesId, instrument, TradeDirection.LONG);
            TitansOrderComment orderComment_sell = new TitansOrderComment(in_type, seriesId, OpenType.P, (int) account.getEquity(), context.getTime(), 0);
            try {
                IOrder order_sell = engine.submitOrder(orderEx_sell.toString(), instrument, IEngine.OrderCommand.SELLSTOP, amount, sell_price, 0, sell_sl, sell_tp, barnextstart - 60000, orderComment_sell.toString());
            } catch (JFException e) {
                err("submit order. error:%s", e.getMessage());
            }
        }
    }

    @Override
    protected void onMessageEx(IMessage message) {
        if (message.getOrder() == null) {
            return;
        }
        IOrder order = message.getOrder();
        OrderEx orderEx = new OrderEx(order, new TitansOrderComment());
        if (!orderEx.valid(getName())) {
            return;
        }
        Instrument instrument = order.getInstrument();
        InstrumentEntity ie = insMap.get(order.getInstrument());
        double amount = order.getAmount();
        double sl = order.getStopLossPrice();
        double tp = order.getTakeProfitPrice();
        double openprice = order.getOpenPrice();
        IEngine.OrderCommand orderCommand = order.getOrderCommand();
        switch (message.getType()) {
            case ORDER_FILL_OK:
                if(log_fill) notif("Titans Fill, %s %s, open_price:%s,amount=%s,tp=%s,sl=%s", instrument.name(), orderCommand.name(), openprice, amount, tp, sl);
                break;
            case ORDER_SUBMIT_OK:
                if (log_submit) {
                    out("Titans Submit, label=%s,open_price=%s,tp=%s,sl=%s,create_time=%s,gtc=%s",
                            order.getLabel(),
                            order.getOpenPrice(),
                            order.getTakeProfitPrice(),
                            order.getStopLossPrice(),
                            CalendarUtils.formatGMTStandard(order.getCreationTime()),
                            CalendarUtils.formatGMTStandard(order.getGoodTillTime()));

                }
                break;
            case ORDER_CLOSE_OK:
                double closePrice = order.getClosePrice();
                double profit = order.getProfitLossInUSD();
                int seriedId = orderEx.orderComment.series_id;
                switch (orderEx.orderComment.series_id) {
                    case 1:
                        profit1 += order.getProfitLossInUSD();
                        break;
                    case 2:
                        profit2 += order.getProfitLossInUSD();
                        break;
                }
                if (profit > 0) {
                    info("%s,Titans %s %s Close,opentime=%s,fill_time=%s, open_price:%s,amount=%s,tp=%s,sl=%s,closePrice=%s,profit=%s,profit.pip=%s",
                            account.getBalance(), instrument.name(), orderCommand.name(),
                            CalendarUtils.formatGMTStandard(order.getCreationTime()), CalendarUtils.formatGMTStandard(order.getFillTime()),
                            openprice, amount, tp, sl, closePrice, profit, order.getProfitLossInPips());
                } else if (profit < 0) {
                    err("%s,Titans %s %s Close, opentime=%s,fill_time=%s, open_price:%s,amount=%s,tp=%s,sl=%s,closePrice=%s,profit=%s,profit.pip=%s",
                            account.getBalance(), instrument.name(), orderCommand.name(),
                            CalendarUtils.formatGMTStandard(order.getCreationTime()), CalendarUtils.formatGMTStandard(order.getFillTime()),
                            openprice, amount, tp, sl, closePrice, profit, order.getProfitLossInPips());
                } else {
//                    if (log_submit)
//                        warn("%s,Titans %s %s Cancel, opentime=%s,open_price:%s,%s,profit=%s", account.getBalance(), instrument.name(), orderCommand.name(), CalendarUtils.formatGMTStandard(order.getCreationTime()), order.getOpenPrice(), message, profit);
                }
                break;
        }
    }

    private static OrderExManager getOrderExManager(TitansConfig baseConfig, List<IOrder> orderList) {

        final OrderExManager o = new OrderExManager();
        for (IOrder order : orderList) {
            TitansOrderComment orderComment = new TitansOrderComment();
            OrderEx orderEx = new OrderEx(order, orderComment);
            if (!orderEx.valid(baseConfig.name, baseConfig.index) || !orderEx.orderComment.valid) {
                continue;
            }

            if (order.getOrderCommand() == IEngine.OrderCommand.BUY && order.getState() == IOrder.State.FILLED) {
                o.buyList.add(orderEx);
                o.buy_pl_usd += order.getProfitLossInUSD();
                o.buy_pl_pip += order.getProfitLossInPips();
                o.buy_c += order.getCommissionInUSD();
                if (o.buy_first == null || orderEx.seriesIndex < o.buy_first.seriesIndex) {
                    o.buy_first = orderEx;
                }
                if (o.buy_last == null || orderEx.seriesIndex > o.buy_last.seriesIndex) {
                    o.buy_last = orderEx;
                }
            } else if (order.getOrderCommand() == IEngine.OrderCommand.SELL && order.getState() == IOrder.State.FILLED) {
                o.sellList.add(orderEx);
                o.sell_pl_usd += order.getProfitLossInUSD();
                o.sell_pl_pip += order.getProfitLossInPips();
                o.sell_c += order.getCommissionInUSD();
                if (o.sell_first == null || orderEx.seriesIndex < o.sell_first.seriesIndex) {
                    o.sell_first = orderEx;
                }
                if (o.sell_last == null || orderEx.seriesIndex > o.sell_last.seriesIndex) {
                    o.sell_last = orderEx;
                }
            } else if (order.getOrderCommand() == IEngine.OrderCommand.SELLLIMIT && order.getState() == IOrder.State.OPENED) {
                o.sellLimitList.add(orderEx);
            } else if (order.getOrderCommand() == IEngine.OrderCommand.BUYLIMIT && order.getState() == IOrder.State.OPENED) {
                o.buyLimitList.add(orderEx);
            } else if (order.getOrderCommand() == IEngine.OrderCommand.BUYSTOP && order.getState() == IOrder.State.OPENED) {
                o.buyStopList.add(orderEx);
            } else if (order.getOrderCommand() == IEngine.OrderCommand.SELLSTOP && order.getState() == IOrder.State.OPENED) {
                o.sellStopList.add(orderEx);
            }
        }
        return o;
    }

    final static class TitansConfig extends BaseConfig {
        final Instrument instrument;//货币

        TitansConfig(String name, int index, double m, double lossLimit, Instrument instrument) {
            super(name, index, m, lossLimit);
            this.instrument = instrument;
        }

        @Override
        public String toString() {
            return "Titans_" + index
                    + "_" + instrument.name()
                    + "_m_" + m;
        }

        @Override
        public boolean isValid() {
            return instrument != null && m > 0;
        }
    }

    final static class TitansOrderComment extends BaseOrderComment {
        public TitansOrderComment() {
        }

        TitansOrderComment(int series_id, int series_index, OpenType type, int first_equit, long first_time, int this_profitloss) {
            super(series_id, series_index, type, first_equit, first_time, this_profitloss);
        }

    }
}
